package com.net.xpay.backend.job;


import com.google.common.base.Strings;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.net.common.util.SerializationUtil;
import com.net.xpay.common.domain.User;
import com.net.xpay.common.domain.UserProfile;
import com.net.xpay.common.domain.notify.SafePayBatchNotifyRecord;
import com.net.xpay.common.domain.openapi.OpenApp;
import com.net.xpay.common.enums.notify.NotifyStatus;
import com.net.xpay.common.manager.UserManager;
import com.net.xpay.common.manager.UserProfileManager;
import com.net.xpay.common.manager.notify.SafePayBatchNotifyRecordManager;
import com.net.xpay.common.manager.openapi.OpenAppManager;
import com.net.xpay.common.util.OpenApiSignatureUtil;
import com.net.xpay.core.helper.SignerCheckHelper;
import com.net.xpay.core.utils.NotifyHttpClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.message.BasicNameValuePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

@Slf4j
@Component
public class SafePayNotifyMainJob implements Runnable {

    @Autowired
    private SafePayNotifyJob safePayNotifyJob;

    @Autowired
    private SafePayBatchNotifyRecordManager safePayBatchNotifyRecordManager;

    @Autowired
    private UserProfileManager userProfileManager;

    @Autowired
    private UserManager userManager;

    @Autowired
    private OpenAppManager openAppManager;

    @Autowired
    private SignerCheckHelper signerCheckHelper;

    private final ExecutorService executorService = Executors.newFixedThreadPool(300, new ThreadFactoryBuilder().setNameFormat("SafePayNotifyMainJob-%d").build());


    @Override
    public void run() {
        while (true) {
            Long id;
            try {
                id = safePayNotifyJob.getWorkQueue().take();
                doNotifyWork(id);
            } catch (Throwable e) {
                log.info("safePayNotifyMainJob error", e);
            }
        }

    }

    private void doNotifyWork(Long id) {
        if (id == null) {
            log.warn("doNotifyWork no id");
            return;
        }

        SafePayBatchNotifyRecord record = safePayBatchNotifyRecordManager.getById(id);

        if (record.getStatus() != NotifyStatus.WAIT || record.getNextNotifyTime().isAfter(LocalDateTime.now())) {
            safePayNotifyJob.remove(id);
            return;
        }
        FutureTask<Void> futureTask = new FutureTask<>(() -> {
            try {
                if (record.getAppId() == null) {
                    //非开放应用
                    doNotifyRecord(record);
                } else {
                    //开放应用
                    doOpenApiNotifyRecord(record);
                }

                safePayNotifyJob.remove(id);
            } catch (Exception e) {
                log.info("doNotifyWork doNotifyRecord error|id={}", id, e);
                safePayNotifyJob.remove(id);
            }

        }, null);
        executorService.submit(futureTask);
    }

    private void doOpenApiNotifyRecord(SafePayBatchNotifyRecord record) {
        UserProfile userProfile = userProfileManager.getById(record.getUserId());
        String notifyUrl = record.getNotifyUrl();
        if (Strings.isNullOrEmpty(notifyUrl)) {
            notifyUrl = userProfile.getSafePayNotifyUrl();
        }
        if (Strings.isNullOrEmpty(notifyUrl)) {
            log.error("doOpenApiNotifyRecord doOpenApiNotifyRecord error|user no profile notifyUrl|profile={}", SerializationUtil.obj2String(userProfile));
            return;
        }
        OpenApp openApp = openAppManager.getById(record.getAppId());
        long t = System.currentTimeMillis();
        String time = t + "";
        Map<String, String> params = new HashMap<>();
        params.put("appId", record.getAppId().toString());
        params.put("timestamp", time);
        params.put("data", record.getNotifyContent());
        String content = OpenApiSignatureUtil.getSignCheckContent(params);
        String sign = OpenApiSignatureUtil.rsaSign(content, openApp.getPrivateKey(), "UTF-8", "RSA2");

        List<NameValuePair> paramList = new ArrayList<>();
        NameValuePair appId = new BasicNameValuePair("appId", record.getAppId().toString());
        paramList.add(appId);
        NameValuePair timestamp = new BasicNameValuePair("timestamp", time);
        paramList.add(timestamp);
        NameValuePair signature = new BasicNameValuePair("sign", sign);
        paramList.add(signature);
        NameValuePair data = new BasicNameValuePair("data", record.getNotifyContent());
        paramList.add(data);

        String resp;
        try {
            HttpPost method = new HttpPost(notifyUrl);
            method.setEntity(new UrlEncodedFormEntity(paramList, "UTF-8"));
            log.info("safePayNotifyMainJob doOpenApiNotifyRecord start|id={}|appId={}|userId={}|params={}", record.getImportBatchNo(), record.getAppId(), record.getUserId(), SerializationUtil.obj2String(paramList));
            resp = NotifyHttpClientUtil.getInstance().executeAndGet(method, null, "UTF-8");
            log.info("safePayNotifyMainJob doOpenApiNotifyRecord cost={}|id={}|appId={}|userId={}|resp={}", System.currentTimeMillis() - t, record.getImportBatchNo(), record.getAppId(), record.getUserId(), resp);
            if (resp != null  && (
                    "ok".equalsIgnoreCase(resp.trim())
                            || "\"ok\"".equalsIgnoreCase(resp.trim())
                            || "success".equalsIgnoreCase(resp.trim())
                            || "\"success\"".equalsIgnoreCase(resp.trim())
            )) {
                record.setStatus(NotifyStatus.OK);
                record.setNotifyCount(record.getNotifyCount() + 1);
                record.setEndTime(LocalDateTime.now());
                safePayBatchNotifyRecordManager.update(record);
            } else {
                record.setNotifyCount(record.getNotifyCount() + 1);
                if (record.getNotifyCount() >= 8) {
                    record.setStatus(NotifyStatus.DEAD);
                    record.setEndTime(LocalDateTime.now());
                    safePayBatchNotifyRecordManager.update(record);
                } else {
                    long[] minutes = new long[]{
                            2, 10, 10, 60, 120, 360, 900
                    };
                    record.setNextNotifyTime(record.getNextNotifyTime().plusMinutes(minutes[(record.getNotifyCount() - 1) % 7]));
                    safePayBatchNotifyRecordManager.update(record);
                }
            }
        } catch (Throwable e) {
            log.error("safePayNotifyMainJob doOpenApiNotifyRecord error cost={}|id={}|appId={}|userId={}", System.currentTimeMillis() - t, record.getImportBatchNo(), record.getAppId(), record.getUserId(), e);
            record.setNotifyCount(record.getNotifyCount() + 1);
            if (record.getNotifyCount() >= 8) {
                record.setStatus(NotifyStatus.DEAD);
                record.setEndTime(LocalDateTime.now());
                safePayBatchNotifyRecordManager.update(record);
            } else {
                long[] minutes = new long[]{
                        2, 10, 10, 60, 120, 360, 900
                };
                record.setNextNotifyTime(record.getNextNotifyTime().plusMinutes(minutes[(record.getNotifyCount() - 1) % 7]));
                safePayBatchNotifyRecordManager.update(record);
            }
        }
    }

    private void doNotifyRecord(SafePayBatchNotifyRecord record) {
        UserProfile userProfile = userProfileManager.getById(record.getUserId());
        String notifyUrl = record.getNotifyUrl();
        if (Strings.isNullOrEmpty(notifyUrl)) {
            notifyUrl = userProfile.getSafePayNotifyUrl();
        }
        if (Strings.isNullOrEmpty(notifyUrl)) {
            log.error("safePayNotifyMainJob doNotifyRecord error|user no profile notifyUrl|profile={}", SerializationUtil.obj2String(userProfile));
            return;
        }
        User user = userManager.getById(record.getUserId());

        List<NameValuePair> paramList = new ArrayList<>();
        NameValuePair appkey = new BasicNameValuePair("appkey", user.getAppkey());
        paramList.add(appkey);
        long t = System.currentTimeMillis();
        String time = t + "";
        NameValuePair timestamp = new BasicNameValuePair("timestamp", time);
        paramList.add(timestamp);
        NameValuePair signature = new BasicNameValuePair("signature", signerCheckHelper.signV2(user.getAppkey(), user.getSecret(), time, record.getImportBatchNo()));
        paramList.add(signature);
        NameValuePair data = new BasicNameValuePair("data", record.getNotifyContent());
        paramList.add(data);

        String resp;
        try {
            HttpPost method = new HttpPost(notifyUrl);
            method.setEntity(new UrlEncodedFormEntity(paramList, "UTF-8"));
            log.info("safePayNotifyMainJob doNotifyRecord start|id={}|userId={}", record.getImportBatchNo(), record.getUserId());
            resp = NotifyHttpClientUtil.getInstance().executeAndGet(method, null, "UTF-8");
            log.info("safePayNotifyMainJob doNotifyRecord cost={}|id={}|userId={}|resp={}", System.currentTimeMillis() - t, record.getImportBatchNo(), record.getUserId(), resp);
            if (resp != null  && (
                    "ok".equalsIgnoreCase(resp.trim())
                            || "\"ok\"".equalsIgnoreCase(resp.trim())
                            || "success".equalsIgnoreCase(resp.trim())
                            || "\"success\"".equalsIgnoreCase(resp.trim())
            )) {
                record.setStatus(NotifyStatus.OK);
                record.setNotifyCount(record.getNotifyCount() + 1);
                record.setEndTime(LocalDateTime.now());
                safePayBatchNotifyRecordManager.update(record);
            } else {
                record.setNotifyCount(record.getNotifyCount() + 1);
                if (record.getNotifyCount() >= 8) {
                    record.setStatus(NotifyStatus.DEAD);
                    record.setEndTime(LocalDateTime.now());
                    safePayBatchNotifyRecordManager.update(record);
                } else {
                    long[] minutes = new long[]{
                            2, 10, 10, 60, 120, 360, 900
                    };
                    record.setNextNotifyTime(record.getNextNotifyTime().plusMinutes(minutes[(record.getNotifyCount() - 1) % 7]));
                    safePayBatchNotifyRecordManager.update(record);
                }
            }
        } catch (Throwable e) {
            log.error("safePayNotifyMainJob doNotifyRecord error cost={}|id={}|userId={}", System.currentTimeMillis() - t, record.getImportBatchNo(), record.getUserId(), e);
            record.setNotifyCount(record.getNotifyCount() + 1);
            if (record.getNotifyCount() >= 8) {
                record.setStatus(NotifyStatus.DEAD);
                record.setEndTime(LocalDateTime.now());
                safePayBatchNotifyRecordManager.update(record);
            } else {
                long[] minutes = new long[]{
                        2, 10, 10, 60, 120, 360, 900
                };
                record.setNextNotifyTime(record.getNextNotifyTime().plusMinutes(minutes[(record.getNotifyCount() - 1) % 7]));
                safePayBatchNotifyRecordManager.update(record);
            }
        }

    }
}

